package com.goblin.model.base;

import com.goblin.common.response.Code;
import com.goblin.common.response.Message;
import com.goblin.common.response.ParamException;
import com.goblin.common.response.Response;
import com.goblin.common.util.TransforEntity;
import com.goblin.model.uaa.po.User;
import com.goblin.security.AuthorityStore;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.CrudRepository;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;

public class BaseController<T> extends ControllerAgency<T>
        implements ApplicationContextAware {

    @Autowired
    protected HttpServletRequest request;

    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        BaseController.context = context;
    }

    public static ApplicationContext getContext() {

        return context;

    }

    //	================================= common method ======================================
    @Override
    @GetMapping("/{id}")
    public Response<T> get(@PathVariable(value = "id") Integer id) {
        Object _obj = null;
        Object result = null;

        String url = request.getRequestURI();
        Response resp = getAgency(url);
        if (resp.getCode().equals(Code.SUCCESS.value)) {
            _obj = resp.getData();
        } else {
            return resp;
        }

        BaseControllerInvocationHandler proxy = new BaseControllerInvocationHandler(_obj);
        CrudRepository crudRepository = (CrudRepository) proxy.CreatProxyedObj();
        Optional optional = crudRepository.findById(id);
        if (optional.isPresent()) {
            result = optional.get();
        }

        after(result);

        if (result == null) {
            return Response.failure(Code.RESOURCE_NOT_FOUND, Message.RESOURCE_NOT_EXISTS);
        }

        return Response.success(result);

    }

    @Override
    @GetMapping("/all")
    public Response<List> getAll() {
        Object _obj = null;

        String url = request.getRequestURI();
        Response resp = getAgency(url);
        if (resp.getCode().equals(Code.SUCCESS.value)) {
            _obj = resp.getData();
        } else {
            return resp;
        }

        BaseControllerInvocationHandler proxy = new BaseControllerInvocationHandler(_obj);
        CrudRepository crudRepository = (CrudRepository) proxy.CreatProxyedObj();
        Iterable iterable = crudRepository.findAll();
        List list = new ArrayList<>();
        iterable.forEach(single -> list.add(single));

//        after(null);
        if (list.size() == 0) {
            return Response.failure(Code.RESOURCE_NOT_FOUND, Message.RESOURCE_NOT_EXISTS);
        }

        return Response.success(list);

    }

    @Override
    @PostMapping
    public Response<T> save(@RequestBody T t) {

        Object _obj = null;
        String url = request.getRequestURI();
        Response resp = getAgency(url);
        if (resp.getCode().equals(Code.SUCCESS.value)) {
            _obj = resp.getData();
        } else {
            return resp;
        }

        try {
            before(t);
        } catch (ParamException ex) {
            return Response.failure(Code.BAD_REQUEST, ex.getMessage());
        }
        //Todo 添加创建人相关信息
        BaseEntity entity = (BaseEntity) t;
        entity.setCreateTime(new Date());
        entity.setStatus("0");
        entity.setVersionCode(0);

        String authorization = request.getHeader("authorization");
        List list = AuthorityStore.getAuth(authorization);
        if (list != null) {
            User user = (User) list.get(1);
            entity.setCreateUser(user.getNickName());
            entity.setCreateUserId(user.getId());
        }

        BaseControllerInvocationHandler proxy = new BaseControllerInvocationHandler(_obj);
        CrudRepository crudRepository = (CrudRepository) proxy.CreatProxyedObj();
        t = (T) crudRepository.save(entity);

        afterSave(t);

        return Response.success(t);

    }

    @Override
    @DeleteMapping
    public Response<T> delete(@RequestParam List<Integer> ids,
                              @RequestParam(required = false, defaultValue = "0") Integer type) {

        Object _obj = null;
        String url = request.getRequestURI();
        Response resp = getAgency(url);
        if (resp.getCode().equals(Code.SUCCESS.value)) {
            _obj = resp.getData();
        } else {
            return resp;
        }

        BaseControllerInvocationHandler proxy = new BaseControllerInvocationHandler(_obj);
        CrudRepository crudRepository = (CrudRepository) proxy.CreatProxyedObj();

        if (type == 0) {
            synchronized (this) {
                for (int i = 0; i < ids.size(); i++) {
                    Optional<BaseEntity> optional = crudRepository.findById(ids.get(i));
                    BaseEntity entity = null;
                    if (!optional.isPresent()) {
                        return Response.failure(Code.RESOURCE_NOT_FOUND,
                                Message.RESOURCE_NOT_EXISTS);
                    }
                    entity = optional.get();

                    entity.setDeleteTime(new Date());
                    // Todo 添加删除人信息
                    entity.setStatus("1");

                    String authorization = request.getHeader("authorization");
                    List list = AuthorityStore.getAuth(authorization);
                    if (list != null) {
                        User user = (User) list.get(1);
                        entity.setUpdateUser(user.getNickName());
                        entity.setUpdateUserId(user.getId());
                    }
                    crudRepository.save(entity);

                    after(entity);
                }
            }
        } else if (type == 1) {
            ids.forEach((e) -> {
                crudRepository.deleteById(e);
            });
        }

        return Response.success();

    }

    @Override
    @PutMapping("/{id}")
    public Response<T> update(@PathVariable Integer id, @RequestBody T t) {

        Object _obj = null;
        String url = request.getRequestURI();
        Response resp = getAgency(url);
        if (resp.getCode().equals(Code.SUCCESS.value)) {
            _obj = resp.getData();
        } else {
            return resp;
        }

        BaseControllerInvocationHandler proxy = new BaseControllerInvocationHandler(_obj);
        CrudRepository crudRepository = (CrudRepository) proxy.CreatProxyedObj();

        Optional<T> optional = crudRepository.findById(id);
        if (!optional.isPresent()) {
            return Response.failure(Code.RESOURCE_NOT_FOUND, Message.RESOURCE_NOT_EXISTS);
        }
        T dao = optional.get();
        BaseEntity entity = (BaseEntity) t;

        if (((BaseEntity) t).getVersionCode() != ((BaseEntity) dao).getVersionCode()) {
            return Response.failure(Code.BAD_REQUEST, Message.DATA_INVALID);
        }

        Class<T> _class = (Class<T>) ((ParameterizedType) getClass()
                .getGenericSuperclass()).getActualTypeArguments()[0];
        t = TransforEntity.copyPropertiesNotNull(_class, dao, t);

        //Todo 添加修改人相关信息
        entity.setUpdateTime(new Date());
        String authorization = request.getHeader("authorization");
        List list = AuthorityStore.getAuth(authorization);
        if (list != null) {
            User user = (User) list.get(1);
            entity.setUpdateUser(user.getNickName());
            entity.setUpdateUserId(user.getId());
        }

        entity.setId(((BaseEntity) (dao)).getId());
        entity.setVersionCode(entity.getVersionCode() + 1);

        before((T)entity);

        entity = (BaseEntity) crudRepository.save(entity);

        after(entity);

        return Response.success(entity);

    }

    @Override
    public Response<T> getPageable(Pageable pageable) {
        return null;
    }

    //====================================================================================
    private Response<Object> getAgency(String url) {
        Object _obj = null;
        try {
            String[] models = url.split("/");
            _obj = context.getBean(models[3] + "Repository");
        } catch (Exception ex) {
            ex.printStackTrace();
            return Response
                    .failure(Code.SERVER_INTERNAL_ERROR, Message.SERVER_INTERNAL_ERROR);
        }
        return Response.success(_obj);
    }
}
